package com.pocket.insight.base.sql;

import com.alibaba.druid.DbType;
import com.pocket.insight.base.context.OffsetLimitContext;
import com.pocket.insight.base.mql.TypedVal;
import com.pocket.insight.base.sql.resultProcessor.WriteResultSetProcessor;
import com.pocket.insight.controller.dto.ConnectionOptionsDto;
import com.pocket.insight.controller.dto.DatasetResultDto;
import com.pocket.insight.controller.dto.PathTableNode;
import com.pocket.insight.entity.Dataset;
import org.apache.commons.lang3.tuple.Pair;

import java.io.Serializable;
import java.sql.Connection;
import java.util.List;
import java.util.Properties;

/**
 * 描述：
 * @author zhaozhile
 */
public interface Vendor extends Serializable, Provider {

    /********************************* 1. 连接基本配置 *********************************/
    DbType getDbType();

    String getDriver();

    String getUrl();

    String getHost();

    Integer getPort();

    Properties getConnectionProperties();

    default void setConnectionConfig(ConnectionOptionsDto optionsDto) {
    }

    /********************************* 2. 数据连接验证、获取相关 *********************************/
    // 验证服务器端口
    void verifySocket();

    // 验证是否支持数据连接版本
    void checkVersionSupported();

    // 验证连接连通性SQL
    default Object getPingSql() {
        return "select 1 as test";
    }

    Connection getNoAutoCommitConnection(String url);

    Connection getNoAutoCommitConnection(String url, Properties properties);

    Connection getUpdateConnection(String url);

    Connection getUpdateConnection(String url, Properties properties);

    List<String> getDefaultTablePath();

    void setPath(Connection connection, List<String> path);

    /******************************** 3. 数据库方言 *********************************/
    Dialect getDialect();

    List<String> rewriterPath(List<String> path);

    String reformTableName(String tableName);

    /******************************** 4. 执行SQL相关 *********************************/
    /**
     * 执行 SQL 获取数据
     * @param path      数据库路径
     * @param query     执行SQL
     * @param addLimit  是否需要添加 limit
     * @return
     */
    default DatasetResultDto fetchRawQueryData(List<String> path, String query, boolean addLimit) {
        return fetchSqlData(query);
    }

    default DatasetResultDto fetchRawQueryData(List<String> path, Object query) {
        return fetchRawQueryData(path, (String) query, false);
    }

    DatasetResultDto fetchSqlData(String fetchSql);













    void setCurrentTransactionConn(Connection connection);

    List<Dataset.Field> getTableSchema(String table);








    List<PathTableNode> getPathTables();

    DatasetResultDto fetchQueryData(TypedVal typedVal);

    default DatasetResultDto fetchTableData(String table) {
        return fetchSqlData(getTableSql(table));
    }

    default DatasetResultDto fetchTableData(List<String> tablePath) {
        return fetchQueryData(getTableSql(tablePath));
    }

    default TypedVal getTableSql(List<String> tablePath) {
        Dialect dialect = getDialect();
        String sql = String.format("SELECT * FROM %s", new Object[]{dialect.quotePath(tablePath)});
        Long limit = OffsetLimitContext.getLimit(Long.valueOf(1000L));
        Long offset = OffsetLimitContext.getOffset(Long.valueOf(0L));
        return dialect.setPage(new TypedVal(sql), offset, limit);
    }

    default String getTableSql(String table) {
        Dialect dialect = getDialect();
        String sql = String.format("SELECT * FROM %s", new Object[]{dialect.quoteTableName(table)});
        Long limit = OffsetLimitContext.getLimit(1000L);
        Long offset = OffsetLimitContext.getOffset(0L);
        return dialect.setPage(new TypedVal(sql), offset, limit).getStr();
    }

    Pair<String, String> reformSql(String tablePrefix, String sql);

    String reformatSpecialFunctionInSql(String sql);

    /********************************* 数据处理相关 *********************************/

    WriteResultSetProcessor getResultSetProcessor();

    default String getConnectionKey() {
        Properties props = getConnectionProperties();
        StringBuilder propsStr = new StringBuilder();
        if (props != null) {
            props.keySet().stream().map(Object::toString).sorted().forEach(one -> propsStr.append(one).append("=").append(props.getProperty(one)).append("&"));
        }
        return getUrl() + "?" + getUrl();
    }

    default void setIfNotNull(Properties info, String key, Object value) {
        if (value != null) {
            info.put(key, value);
        }
    }

    default boolean isSqlAvailable() {
        return true;
    }


    boolean isFieldCaseSensitive();

    DatasetResultDto fetchSqlDataWithThreadCache(String sql);
}
